// Copyright 2013 The Go Circuit Project
// Use of this source code is governed by the license for
// The Go Circuit Project, found in the LICENSE file.
//
// Authors:
//   2013 Petar Maymounkov <p@gocircuit.org>

package shutter

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"sync"

	"circuit/kit/iomisc"
	"circuit/kit/rh/ns"
)

// commander is an ns.InterfaceDir interfacing to job control and concurrency resources
type commander struct {
	z_    sync.Mutex
	z_env []string
	z_dir string
}

//
func newCommander() *commander {
	wd, _ := os.Getwd()
	return &commander{
		z_env: os.Environ(),
		z_dir: wd,
	}
}

//
func (y *commander) Env(q *ns.RequestReader, p *ns.ResponseWriter) {
	y.z_.Lock()
	defer y.z_.Unlock()
	// Read from FS user's writes
	var lines []string
	scanner := bufio.NewScanner(q)
	for scanner.Scan() {
		l := strings.TrimSpace(scanner.Text())
		if l == "" {
			continue
		}
		lines = append(lines, l)
	}
	if err := scanner.Err(); err != nil {
		log.Printf("env scanner (%s)", err)
		return
	}
	//
	if len(lines) > 0 {
		y.z_env = lines
	}
	// Write back for FS user to read
	for _, l := range y.z_env {
		p.WriteString(l + "\n")
	}
}

//
func (y *commander) Dir(q *ns.RequestReader, p *ns.ResponseWriter) {
	y.z_.Lock()
	defer y.z_.Unlock()
	// Read from FS user's writes
	var line string
	scanner := bufio.NewScanner(q)
	for scanner.Scan() {
		line = strings.TrimSpace(scanner.Text())
		break
	}
	// Do
	var err error
	if line != "" {
		if err = os.Chdir(line); err == nil {
			y.z_dir = line
		}
	}
	// Write back for FS user to read
	p.WriteString(y.z_dir + "\n")
	if err != nil {
		p.WriteString(fmt.Sprintf("chdir (%s)\n", err.Error()))
	}
}

func (y *commander) direnv() (string, []string) {
	y.z_.Lock()
	defer y.z_.Unlock()
	//
	env := make([]string, len(y.z_env))
	copy(env, y.z_env)
	return y.z_dir, env
}

//
func (y *commander) Sh(q *ns.RequestReader, p *ns.ResponseWriter) {
	// Do
	log.Println("sh enter")
	cmd := exec.Command("sh")
	cmd.Dir, cmd.Env = y.direnv()
	//
	stdin, err := cmd.StdinPipe()
	if err != nil {
		p.Errorf("stdin (%s)", err)
	}
	//
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		p.Errorf("stdout (%s)", err)
	}
	//
	stderr, err := cmd.StderrPipe()
	if err != nil {
		p.Errorf("stderr (%s)", err)
	}
	log.Println("sh forward")

	// Forward and close at end
	iomisc.SniffClose("proc⋅stdin ⟽ file", stdin, q, nil)
	iomisc.SniffClose("proc⋅stdoe ⟾ file", p, iomisc.Combine(stdout, stderr), nil)

	// Exec
	log.Println("sh run")
	if err = cmd.Run(); err != nil {
		// XXX // close forwarding here
		log.Printf("sh run (%s)", err)
		p.Errorf("sh run (%s)", err)
	}
	log.Println("sh ok")
}
